/****************************************************
	文件：ScheduleMgr.cs
	作者：HuskyT
	邮箱：1005240602@qq.com
	日期：2020/7/12 13:17:20
	功能：规划器Mgr
*****************************************************/

using System;
using System.Collections.Generic;
using System.Threading;

namespace HTSpider
{
    public class ScheduleMgr : Singleton<ScheduleMgr>
    {
        private Queue<Req> mImmediateQue;
        private Queue<Req> mHighQue;
        private Queue<Req> mMidQue;
        private Queue<Req> mLowQue;
        private Dictionary<Req, Thread> mThreadDict;
        private static readonly object mImmediateQueLock = new object();
        private static readonly object mHighQueLock = new object();
        private static readonly object mMidQueLock = new object();
        private static readonly object mLowQueLock = new object();
        private static readonly object mThreadDictLock = new object();

        public int FreeThreadCount
        {
            get
            {
                return Consts.THREAD_MAX_COUNT - mThreadDict.Count;
            }
        }

        public void Init()
        {
            mImmediateQue = new Queue<Req>();
            mHighQue = new Queue<Req>();
            mMidQue = new Queue<Req>();
            mLowQue = new Queue<Req>();
            mThreadDict = new Dictionary<Req, Thread>();
        }
        public void AddReq(Req req)
        {
            switch (req.Priority)
            {
                case Enums.ReqPriority.Immediate:
                    mImmediateQue.Enqueue(req);
                    break;
                case Enums.ReqPriority.High:
                    mHighQue.Enqueue(req);
                    break;
                case Enums.ReqPriority.Mid:
                    mMidQue.Enqueue(req);
                    break;
                case Enums.ReqPriority.Low:
                    mLowQue.Enqueue(req);
                    break;
            }
            Logger.LogFormat("添加优先度为{0}的Request到规划器，URL：{1}", req.Priority, req.Url);
        }

        public void Tick()
        {
            while (mImmediateQue.Count > 0 && FreeThreadCount > 0)
            {
                Req req = null;
                lock (mImmediateQueLock)
                {
                    if (mImmediateQue.Count > 0)
                    {
                        req = mImmediateQue.Dequeue();
                    }
                }
                StartNewThread(req);
            }
            while (mHighQue.Count > 0 && FreeThreadCount > 0)
            {
                Req req = null;
                lock (mHighQueLock)
                {
                    if (mHighQue.Count > 0)
                    {
                        req = mHighQue.Dequeue();
                    }
                }
                StartNewThread(req);
            }
            while (mMidQue.Count > 0 && FreeThreadCount > 0)
            {
                Req req = null;
                lock (mMidQueLock)
                {
                    if (mMidQue.Count > 0)
                    {
                        req = mMidQue.Dequeue();
                    }
                }
                StartNewThread(req);
            }
            while (mLowQue.Count > 0 && FreeThreadCount > 0)
            {
                Req req = null;
                lock (mLowQueLock)
                {
                    if (mLowQue.Count > 0)
                    {
                        req = mLowQue.Dequeue();
                    }
                }
                StartNewThread(req);
            }
        }

        private void StartNewThread(Req req)
        {
            if (req == null) return;
            req.Time = DateTime.Now;
            Thread newThread = new Thread(() =>
            {
                EngineMgr.Instance.DoReq(req, (rsp) =>
                {
                    lock (mThreadDictLock)
                    {
                        Thread thread = mThreadDict[rsp.Request];
                        if (thread != null)
                        {
                            mThreadDict[rsp.Request].Abort();
                            mThreadDict[rsp.Request] = null;
                            mThreadDict.Remove(req);
                            Logger.LogFormat("耗时：{0}秒，移除{1}的线程\n当前活跃线程数：{2}，剩余空闲线程数：{3}", rsp.CostTime / 1000, req.Url, mThreadDict.Count, FreeThreadCount);
                        }
                        else
                        {
                            Logger.WarningFormat("移除{0}的线程失败", req.Url);
                        }
                    }
                });
            });
            newThread.IsBackground = true;
            newThread.Start();
            lock (mThreadDictLock)
            {
                mThreadDict.Add(req, newThread);
            }
            Logger.LogFormat("优先级--{0}：为{1}开启新线程\n当前活跃线程数：{2}，剩余空闲线程数：{3}", req.Priority, req.Url, mThreadDict.Count, FreeThreadCount);
        }
    }
}
